---
title: Faults and error pipeline
summary: Fault contracts, error handlers, service behaviors, and what a caller receives when an operation fails.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-22
---

# Faults and error pipeline

All six services share one uniform, purely observational fault pipeline: a `[LogErrorBehavior]` service behavior installs an `ErrorLogErrorHandler` on every channel dispatcher, that handler logs the exception and returns `false`, and because `includeExceptionDetailInFaults="true"` the caller then receives the framework's default fault carrying the full exception type, message, and stack trace. No operation declares a fault contract, and the one explicit `FaultException` construction in the host is unreachable dead code.

## Fault surface summary

| Aspect | Finding |
| --- | --- |
| Declared fault contracts | None — no `[FaultContract]` anywhere in this host |
| Service behavior attributes | `[LogErrorBehavior]` on all 6 service classes |
| `IErrorHandler` implementations | `ErrorLogErrorHandler` (`src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs`) |
| `includeExceptionDetailInFaults` | true (`src/WCF/SAIF.Services.WCF.Host/Web.Base.config`) |
| Effect on callers | Every unhandled exception reaches the caller as a generic SOAP fault carrying full server-side exception detail — type, message, and stack trace |

## Service behaviors

| Behavior | Applied to | Declared in | Effect |
| --- | --- | --- | --- |
| `[LogErrorBehavior]` | `FileService`, `PreferenceService`, `WebContextService`, `ImagingService`, `FaxService`, `SecurityService` | `src/WCF/SAIF.Services.WCF.Host/Extensions/LogErrorBehaviorAttribute.cs` | Installs `ErrorLogErrorHandler` on every channel dispatcher through `ApplyDispatchBehavior` |
| `<serviceDebug includeExceptionDetailInFaults="true"/>` | All services | `Web.Base.config` `<serviceBehaviors>` | Every fault reaching a caller carries full exception detail, regardless of what `ErrorLogErrorHandler` does |
| `<serviceMetadata httpGetEnabled="true"/>` | All services | `Web.Base.config` `<serviceBehaviors>` | WSDL is published per route via `?wsdl` |

## Error handler

| Field | Value |
| --- | --- |
| **Type** | `ErrorLogErrorHandler` : `IErrorHandler` |
| **Source** | `src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs` |
| **Installed by** | `LogErrorBehaviorAttribute` : `IServiceBehavior` — `ApplyDispatchBehavior` |
| **Scope** | Every channel dispatcher on every one of the six service classes |
| **`HandleError` return** | `false` — the exception is not considered handled |
| **`ProvideFault` behavior** | Empty method body — leaves the fault unchanged; WCF produces the default fault |

`HandleError` builds a `SAIF.Core.Contracts.ApplicationExceptionDetail`, hard-coding `Application = "WCF"` and reading the environment track, machine name, and calling user from a freshly constructed `WindowsEnvironmentService`, then passes it to `new LogService(environmentService).LogException(exceptionDetail)`. It then returns `false`, so the framework continues to the standard fault path unchanged. Because `ProvideFault` has an empty body, the handler never replaces or reshapes the outgoing fault message — the pair is purely observational: it logs and never suppresses, transforms, or shapes a fault.

### Logged exception detail

| Field | Value written | Source of the value |
| --- | --- | --- |
| Application | `"WCF"` (hard-coded) | Literal in `ErrorLogErrorHandler.HandleError` |
| Environment / machine / user | Environment track, machine name, calling user | `new WindowsEnvironmentService()` |
| Exception detail | `error.GetErrorLogFormattedException()`, the failing method signature, and the calling user | `Exception`, `error.TargetSite` |

| Aspect | Finding |
| --- | --- |
| Log surface | `LogService` (`new LogService(environmentService).LogException(exceptionDetail)`) |
| Destination | Not determined from source — `LogService` is supplied by the external framework closure |
| Failure of the logger itself | `HandleError` dereferences `error.TargetSite.DeclaringType` with no null guard; `TargetSite` is null for some exception shapes, in which case logging itself would throw inside the error handler (see [Tech debt](tech-debt.md)) |

Exception messages originating from `FileService` and `PreferenceService` can contain caller-supplied path and user values, so those values reach this central exception log alongside the calling user identity.

## Error pipeline diagram

The following diagram visualizes how an exception becomes a fault.

```mermaid
flowchart TD
    OP["Operation throws an exception"] --> DISP["Channel dispatcher"]
    DISP --> HANDLER["ErrorLogErrorHandler.HandleError"]
    HANDLER --> LOG["Exception detail logged<br>via LogService"]
    HANDLER --> RET["HandleError returns false"]
    RET --> DEFAULT["WCF produces the default fault"]
    DISP --> PROVIDE["ErrorLogErrorHandler.ProvideFault<br>empty body, fault unchanged"]
    PROVIDE --> DEFAULT
    DEFAULT --> DETAIL["includeExceptionDetailInFaults=true<br>type, message, stack trace included"]
    DETAIL --> CALLER["Caller receives SOAP fault<br>with full exception detail"]
```

## Declared fault contracts

No operation in this host declares a fault contract. Every failure reaches the caller as a generic SOAP fault whose content is determined by the error pipeline described above. The one explicit `FaultException` construction in the host — `ImagingService.ThrowTimeoutException(int)`, building a `FaultCode("RequestTimeout")` — is private, has no call site anywhere in the host, and is unreachable dead code; no caller ever receives it (see [Tech debt](tech-debt.md)).

## Failure modes by operation

| Operation | Failure | What the caller receives | Detail exposed |
| --- | --- | --- | --- |
| [`ReadFile`](contracts/file-service.md#readfile) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`ReadEntireFile`](contracts/file-service.md#readentirefile) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`WriteFile`](contracts/file-service.md#writefile) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`DeleteFile`](contracts/file-service.md#deletefile) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`Length`](contracts/file-service.md#length) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetPreferences`](contracts/preference-service.md#getpreferences) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`SavePreferences`](contracts/preference-service.md#savepreferences) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetDefaultPrinter`](contracts/preference-service.md#getdefaultprinter) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`Get`](contracts/web-context-service.md#get) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`Save`](contracts/web-context-service.md#save) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`CreateContext`](contracts/web-context-service.md#createcontext) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`QueueImage`](contracts/imaging-service.md#queueimage) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`ReserveImageId`](contracts/imaging-service.md#reserveimageid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`SendFax`](contracts/fax-service.md#sendfax) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`IsAuthorized`](contracts/security-service.md#isauthorized) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`IsAuthorized2`](contracts/security-service.md#isauthorized2) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetUser`](contracts/security-service.md#getuser) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetSaifUserByUserId`](contracts/security-service.md#getsaifuserbyuserid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetSaifUserByEmployeeId`](contracts/security-service.md#getsaifuserbyemployeeid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetUserIdForEmployeeId`](contracts/security-service.md#getuseridforemployeeid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetExternalUser`](contracts/security-service.md#getexternaluser) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetExternalUserByCustomerId`](contracts/security-service.md#getexternaluserbycustomerid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetInjuredWorkerUserByCustomerId`](contracts/security-service.md#getinjuredworkeruserbycustomerid) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |
| [`GetUsersByGroup`](contracts/security-service.md#getusersbygroup) | Unhandled exception in the operation body | Generic SOAP fault | Type, message, and stack trace (`includeExceptionDetailInFaults="true"`) |

## Swallowed failures

| Location | Behavior | Consequence |
| --- | --- | --- |
| `SecurityService.LogRequestDetails` (`src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs`) | Writes a JSON fragment of the operation name and arguments to the IIS log via `HttpContext.Current.Response.AppendToLog(msg)`, wrapped so a logging failure does not fault the caller | A logging failure is invisible to both the caller and the operator; the intended `LOG_REQUEST_DETAILS` kill switch is never consulted either (see [Tech debt](tech-debt.md)) |
| `SecurityService` constructor `appSettings` reads (`src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs`) | `ConfigurationManager.AppSettings` reads for `LOG_REQUEST_DETAILS` and `LDAP_EXTERNAL_KEY` are each wrapped in an empty `try`/`catch` | A missing or misconfigured key fails silently; `ldap_external_key` stays null and the external LDAP host silently falls back to a literal default |

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Activation and hosting](activation-hosting.md)
- [Operations](operations.md)
- [Authentication and authorization](authentication-authorization.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Data model](data-model.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

## Sources
- src/WCF/SAIF.Services.WCF.Host/Extensions/ErrorLogErrorHandler.cs
- src/WCF/SAIF.Services.WCF.Host/Extensions/LogErrorBehaviorAttribute.cs
- src/WCF/SAIF.Services.WCF.Host/Web.Base.config
- src/WCF/SAIF.Services.WCF.Host/Services/ImagingService.cs
- src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs
